import axios, { AxiosError } from "axios"
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import { useUserStore } from "@/stores/userStore";
import router from "@/router";

interface XtxAxiosInstance extends AxiosInstance {
    request<T = any, D = any>(config: AxiosRequestConfig<D>): Promise<T>
}

export default class XtxRequestManager {
    //获取user仓库对象
    private _userStore = useUserStore()
    //用于保存单例对象的静态属性
    //该属性不允许开发者直接访问，所以将该属性设置为私有属性，开发者必须通过instance静态方法获取单例对象
    //因为该属性需要被instance静态方法访问  所以将该属性设置为静态属性
    private static _singletonInstance: XtxRequestManager;

    //用于保存axios实例对象
    private readonly _instance: XtxAxiosInstance;

    //请求基准地址
    // public static baseUrl: string = "https://apipc-xiaotuxian-front.itheima.net/"
    public static baseUrl: string = "https://pcapi-xiaotuxian-front-devtest.itheima.net/"

    //将构造函数设置为私有，防止开发者直接实例化 XtxRequestManager
    private constructor() {
        //创建axios实例对象专门用于小兔仙儿项目发送请求
        this._instance = axios.create({ baseURL: XtxRequestManager.baseUrl })
        // 注册请求拦截器, 在请求发送之前在请求头中加入 token
        // 由于注册到 use 方法中的参数方法, 在内部被直接调用, 所以内部的 this 指向 undefined (严格模式下)
        // 所以此处通过 bind 让方法内部的 this 指向当前类的实例
        this._instance.interceptors.request.use(this._addTokenToRequestHeader.bind(this))
        //注册响应拦截器
        this._instance.interceptors.response.use(
            //剥离axios响应对象，直接返回服务端的响应状态（成功）
            this._peelOffAxiosResponse,
            //登录状态失效，清空用户信息，跳转登录页面（失败）
            this._unauthorized.bind(this)
        )
    }


    //获取单例对象的静态方法
    static get instance() {
        //判断单例是否存在
        if (typeof XtxRequestManager._singletonInstance === "undefined") {
            //单例不存在，创建单例对象
            XtxRequestManager._singletonInstance = new XtxRequestManager()
        }
        //单例存在，返回单例对象
        return XtxRequestManager._singletonInstance

    }

    //在请求头中加入token
    private _addTokenToRequestHeader(config: AxiosRequestConfig) {
        //从userStore中获取token
        const token = this._userStore.profile.result.token;
        //如果token存在将token添加到请求头对象中
        if (token) config.headers = { Authorization: `Bearer ${token}` };
        //返回配置对象 使其生效
        return config;
    }

    //剥离axios响应对象，直接返回服务端的响应状态（成功）
    private _peelOffAxiosResponse(response: AxiosResponse) {
        return response.data;
    }
    //登录状态失效，清空用户信息（失败）
    private _unauthorized(error: unknown) {
        if (error instanceof AxiosError) {
            //如果请求都没发送成功，即请求都没发送出去，有可能是客户端发送的请求报错，那就没有response属性
            //如果请求发送成功，反而是服务端返回了失败状态，那就有response属性
            if (error.response?.status === 401) {
                //清空用户数据
                this._userStore.$reset()

                //跳转到登录页面
                router.push(`/login?return=${encodeURIComponent(router.currentRoute.value.fullPath)}`)
            }
        }
        return Promise.reject(error)
    }


    //用于发送请求的方法
    public request<T = any, D = any>(config: AxiosRequestConfig<D>): Promise<T> {
        return this._instance.request<T, D>(config)
    }
}

